package com.edol.painting.painting;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;

import com.doodle.common.utils.Utils;
import com.edol.painting.R;

public class Brush {

	public static final int BRUSH_ERASER = 0;
	public static final int BRUSH_PEN = 1;
	public static final int BRUSH_PENCIL = 2;
	public static final int BRUSH_MARKER = 3;
	public static final int BRUSH_WATERCOLOR = 4;
	public static final int BRUSH_SOFT_CHARCOAL = 5;
	public static final int BRUSH_CHALK = 6;
	public static final int BRUSH_SOFT_ERASRE = 7;
	public static final int BRUSH_LAST = BRUSH_SOFT_ERASRE;
	public static final int BRUSH_COUNT = BRUSH_LAST + 1;
	
	public static final int EFFECT_TYPE_NORMAL = 0;
	public static final int EFFECT_TYPE_PRESSURE = 1;
	public static final int EFFECT_TYPE_FADE = 2;
	public static final int EFFECT_TYPE_PRESSURE_FADE = 3;
	public static final int EFFECT_TYPE_LIGHT_FADE = 4;
	public static final int EFFECT_TYPE_LIGHT_PRESSURE_FADE = 5;
	
	public static final int TARGET_PAINT = 0;
	public static final int TARGET_PLAYBACK = 1;
	public static final int TARGET_PREVIEW = 2;
	public static final int TARGET_COUNT = 3;
	
	public static final int TEXTURE_MIN_ALPHA = 0;
	public static final int TEXTURE_MAX_ALPHA = 1;
	
	protected Context mContext;
	protected final int mType;
	protected int mWidth;
	protected int mColor;
	protected int mAlpha;
	protected int mStep;
	protected int mEffectType;

	protected Paint mPaint;
	protected Bitmap mLayer;
	protected Bitmap mPaper;
	protected Canvas mLayerCanvas;
	protected int mLayerWidth;
	protected int mLayerHeight;
	protected Route mRoute;
	protected Rect mBounds;
	
	protected static final float WIDTH_SCALE_FACTOR = 0.4f;
	protected static final float WIDTH_PRESSURE_FACTOR = 0.3f;

	protected Brush(Context context, int type) {
		mContext = context;
		mType = type;
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mRoute = new Route();
		mBounds = new Rect();
	}
	
	public void setColor(int color) {
		mColor = color;
		mPaint.setColor(mColor);
	}
	
	public void setWidth(int width) {
		mWidth = width;
	}
	
	public void setAlpha(int alpha) {
		mAlpha = alpha;
	}
	
	public int getType() {
		return mType;
	}
	
	public int getColor() {
		return mColor;
	}
	
	public int getWidth() {
		return mWidth;
	}
	
	public int getAlpha() {
		return mAlpha;
	}
	
	public Bitmap getPaper() {
		return mPaper;
	}
	
	public static Brush copyBrush(Context context, Brush src, int target) {
		if (src == null) return null;
		Brush dst = getBrush(context, target, src.getType());
		dst.setColor(src.getColor());
		dst.setWidth(src.getWidth());
		dst.setAlpha(src.getAlpha());
		dst.setPaper(src.getPaper());
		return dst;
	}
	
	public void setEffectType(int effectType) {
		mEffectType = effectType;
	}

	public void setBrushLayer(Bitmap layer, Canvas layerCanvas) {
		mLayer = layer;
		mLayerCanvas = layerCanvas;
		if (mLayer != null) {
			mLayerWidth = mLayer.getWidth();
			mLayerHeight = mLayer.getHeight();
		}
	}
	
	public void setPaper(Bitmap paper) {
		mPaper = paper;
	}
	
	public void clearBrushLayer(Rect bounds) {
		mLayerCanvas.save(Canvas.CLIP_SAVE_FLAG);
	   if (bounds != null) {
		   bounds.set(bounds.left - 5, bounds.top - 5, bounds.right + 5, bounds.bottom + 5);
		   mLayerCanvas.clipRect(bounds);
	   }
		mLayerCanvas.drawColor(0x0, Mode.CLEAR);
		mLayerCanvas.restore();
	}
	
	protected void drawParticle(Canvas canvas, Particle particle, Rect bounds) {
	}
	
	public void beginRoute(Canvas canvas, Particle particle, Rect bounds) {
		if (canvas != null && particle != null && bounds != null) {
			mRoute.add(particle);
			if (mEffectType == EFFECT_TYPE_PRESSURE) {
				particle.width = mWidth * WIDTH_PRESSURE_FACTOR;
				particle.alpha = mAlpha;
			} else if (mEffectType == EFFECT_TYPE_FADE) {
				particle.width = mWidth;
				particle.alpha = 0;
			} else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
				particle.width = mWidth * WIDTH_PRESSURE_FACTOR;
				particle.alpha = 0;
			} else if (mEffectType == EFFECT_TYPE_LIGHT_FADE) {
				particle.width = mWidth;
				particle.alpha = mAlpha * 2 / 3;
			} else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
				particle.width = mWidth * WIDTH_PRESSURE_FACTOR;
				particle.alpha = mAlpha * 2 / 3;
			} else {
				particle.width = mWidth;
				particle.alpha = mAlpha;
			}
		}
	}

	public void drawInsertPic(Bitmap bitmap, Matrix matrix, Paint paint) {
	    if (mLayerCanvas != null) {
	        mLayerCanvas.drawBitmap(bitmap, matrix, paint);
	    }
	}

	public void drawFillTexture(Path path, Paint paint, int color) {
        if (mLayerCanvas != null) {
            mLayerCanvas.drawPath(path, paint);
            mLayerCanvas.drawColor(color, Mode.SRC_IN);
        }
    }
	
	public void drawRoute(Canvas canvas, Particle particle, Rect bounds) {
		if (canvas != null && particle != null && bounds != null) {
			mRoute.add(particle);
			int count = mRoute.size();
			if (count < 2) {
				return;
			}
			Route result = null;
			if (count == 2) {
				Particle p0 = mRoute.get(0);
				Particle p1 = mRoute.get(1);
				if (mEffectType == EFFECT_TYPE_PRESSURE) {
					p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
					p1.alpha = mAlpha;
				} else if (mEffectType == EFFECT_TYPE_FADE) {
					p1.width = mWidth;
					p1.alpha = 0;
				} else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
					p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
					p1.alpha = 0;
				} else if (mEffectType == EFFECT_TYPE_LIGHT_FADE) {
					p1.width = mWidth;
					p1.alpha = mAlpha * 2 / 3;
				} else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
					p1.width = Utils.clamp(p0.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
					p1.alpha = mAlpha * 2 / 3;
				} else {
					p1.width = mWidth;
					p1.alpha = mAlpha;
				}
				Particle midPoint = Particle.getMidPoint(p0, p1);
				result = Route.calculateLinePoints(p0, midPoint, Math.max(1, mWidth / 4));
			} else {
				Particle p0 = mRoute.get(count - 3);
				Particle p1 = mRoute.get(count - 2);
				Particle p2 = mRoute.get(count - 1);

				if (mEffectType == EFFECT_TYPE_PRESSURE) {
					p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
				} else if (mEffectType == EFFECT_TYPE_FADE) {
					p2.width = mWidth;
				} else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
					p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
				} else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
					p2.width = Utils.clamp(p1.width + mWidth * WIDTH_SCALE_FACTOR, 0, mWidth);
				} else {
					p2.width = mWidth;
				}
				p2.alpha = mAlpha;
				result = Route.calculateSmoothLinePoints(p0, p1, p2,  Math.max(1, mWidth / 4));
			}
			doDraw(canvas, result, bounds);
		}
	}

	protected void doDraw(Canvas canvas, Route result, Rect bounds) {
		mBounds.setEmpty();
		for (Particle p : result) {
			drawParticle(canvas, p, mBounds);
		}
		if (!mBounds.isEmpty()) {
			bounds.union(mBounds);
		}
	}
	
	public void endRoute(Canvas canvas, Particle particle, Rect bounds) {
		if (canvas != null && particle != null && bounds != null) {
			mRoute.add(particle);
			if (canvas != null && mRoute != null) {
				int count = mRoute.size();
				if (count >= Route.ROUTE_BEGIN_INDEX) {
					Particle p0 = mRoute.get(count - 3);
					Particle p1 = mRoute.get(count - 2);
					Particle p2 = mRoute.get(count - 1);
					if (mEffectType == EFFECT_TYPE_PRESSURE) {
						p2.width = 0;
						p2.alpha = mAlpha;
					} else if (mEffectType == EFFECT_TYPE_FADE) {
						p2.width = mWidth;
						p1.alpha = p0.alpha / 2;
						p2.alpha = -p1.alpha;
					} else if (mEffectType == EFFECT_TYPE_PRESSURE_FADE) {
						p2.width = 0;
						p1.alpha = p0.alpha / 2;
						p2.alpha = -p1.alpha;
					} else if (mEffectType == EFFECT_TYPE_LIGHT_FADE) {
						p2.width = mWidth;
						p1.alpha = p0.alpha / 2;
						p2.alpha = 0;
					} else if (mEffectType == EFFECT_TYPE_LIGHT_PRESSURE_FADE) {
						p2.width = 0;
						p1.alpha = p0.alpha / 2;
						p2.alpha = 0;
					} else {
						p2.width = mWidth;
						p2.alpha = mAlpha;
					}
					Route result = Route.calculateSmoothLinePoints(p0, p1, p2,  Math.max(1, mWidth / 5));
					doDraw(canvas, result, bounds);
				}
			}
		}
		mRoute.clear();
	}
	
	private static Brush sBrushes[][] = new Brush[TARGET_COUNT][BRUSH_COUNT];
	
	public static Brush getBrush(Context context, int target, int type) {
        if (type > 7) type = 7;
		Brush brush = sBrushes[target][type];
		if (brush == null) {
			brush = createBrushInternal(context, type);
			sBrushes[target][type] = brush;
		}
		return brush;
	}

    public void clear() {
        for (int i = 0; i < sBrushes.length; i ++) {
            for (int j = 0; j < sBrushes[i].length; j++) {
                Brush brush = sBrushes[i][j];
                if (brush != null) {
                    sBrushes[i][j] = null;
                }
            }
        }
    }
	
	private static Brush createBrushInternal(Context context, int type) {
		Brush brush = null;
		switch (type) {
		case BRUSH_ERASER:
			brush = new EraserBrush(context);
			break;
		case BRUSH_SOFT_ERASRE:
			brush = new SoftEraserBrush(context);
			break;
		case BRUSH_PEN:
			brush = new PenBrush(context);
			break;
		case BRUSH_PENCIL:
			TextureBrush pencil = new TextureBrush(context, Brush.BRUSH_PENCIL);
			pencil.loadBrush(R.raw.pencil);
			brush = pencil;
			break;
		case BRUSH_MARKER:
			brush = new MarkerBrush(context);
			break;
		case BRUSH_WATERCOLOR:
			brush = new WaterColorBrush(context);
			break;
		case BRUSH_SOFT_CHARCOAL:
			TextureBrush softCharcoal = new TextureBrush(context, Brush.BRUSH_SOFT_CHARCOAL);
			softCharcoal.loadBrush(R.raw.soft_charcoal);
			brush = softCharcoal;
			break;
		case BRUSH_CHALK:
			TextureBrush chalk = new TextureBrush(context, Brush.BRUSH_CHALK);
			chalk.loadBrush(R.raw.chalk);
			brush = chalk;
			break;
		}
		return brush;
	}
}
